package com.csx.base.concurrency.atomic;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p> what does this class do ? </p>
 *   该案例包含AtomicInteger基本类型原子操作类的使用
 *   1. 自增场景:计数器 incrementAndGet() decrementAndGet() 的使用
 *   2. 存取款场景 addAndGet(int delta)
 *   描述: 案例1: 自增场景
 *     本案例对一个原子整数类型的变量进行多线程自增操作，
 *     启动了三个线程同时操作该原子整数类型的变量。其中两个线程对该变量累增1000次，另外一个线程对该变量累减500次，
 *     三个线程同时进行，来验证该原子变量自增的线程安全性
 *   描述: 案例2：存取款场景
 *     本案例以一个原子整数类型的变量作为目标
 *     启动了20个线程操作该原子整数类。其中10个线程存款300，10个线程取款200
 *     20个线程同时进行，来验证该变量自增的线程安全性
 *
 * <p> How does it work ?</p>
 * <p> Why does it do this ?</p>
 *
 * @author cuisongxu
 * @date 2024/12/3 周二 9:26
 */
public class AtomicIntegerTest {

    // 用于自增场景的基本类型的原子类
    private final AtomicInteger counter = new AtomicInteger(0);

    // 用于存款取款场景的基本类型的原子类
    private final AtomicInteger balance = new AtomicInteger(10000);

    private final ExecutorService executorService =
            new ThreadPoolExecutor(
                    5,
                    10,
                    300,
                    TimeUnit.SECONDS,
                    new ArrayBlockingQueue<>(100),
                    new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {
        AtomicIntegerTest atomicIntegerTest = new AtomicIntegerTest();
//        atomicIntegerTest.testAtomicAdder();

        atomicIntegerTest.testAtomicDepositWithdraw();
    }

    // 存取测试
    public void testAtomicDepositWithdraw() {
        AtomicDepositWithdraw atomicDepositWithdraw = new AtomicDepositWithdraw();
        // 提交10个存款任务,交给线程池的多个线程并发执行
        for (int i = 0; i < 10; i++) {
            executorService.submit(() -> atomicDepositWithdraw.deposit(300));
        }
        // 提交10个取款任务,交给线程池的多个线程并发执行
        for (int i = 0; i < 10; i++) {
            executorService.submit(() -> atomicDepositWithdraw.withdraw(200));
        }

        // 等待所欲任务执行完毕
        executorService.shutdown();
        while (!executorService.isTerminated()) {
            try {
                TimeUnit.MILLISECONDS.sleep(20);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        System.out.println("最终账户余额: " + balance.get() + " 是否满足预期: " + (balance.get() == 11000 ? "是" : "否"));
    }


    // 原子自增测试
    public void testAtomicAdder() {

        Thread adder1 = new Thread(new AtomicAdder(1000, true), "adder1");
        Thread adder2 = new Thread(new AtomicAdder(1000, true), "adder2");
        Thread reducer1 = new Thread(new AtomicAdder(500, false), "reducer1");

        adder1.start();
        adder2.start();
        reducer1.start();

        try {
            // 等待所有线程执行完毕
            adder1.join();
            adder2.join();
            reducer1.join();

            System.out.println("累加结果: " + counter.get() + " 是否符合预期: " + (1500 == counter.get() ? "是" : "否"));
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

    }

    class AtomicDepositWithdraw {

        // 存款
        public void deposit(int amount) {
            balance.addAndGet(amount);
            System.out.println(Thread.currentThread().getName() + "存款 " + amount + ", 当前余额: " + balance.get());
        }

        // 取款
        public void withdraw(int amount) {
            balance.addAndGet(-amount);
            System.out.println(Thread.currentThread().getName() + "取款 " + amount + ", 当前余额: " + balance.get());
        }
    }

    class AtomicAdder implements Runnable {

        // 累增次数
        private final int increaseTimes;

        // 是否为自增，默认为true;如果为false,表示自减
        private boolean isAdd = true;

        public AtomicAdder(int increaseTimes, boolean isAdd) {
            this.increaseTimes = increaseTimes;
            this.isAdd = isAdd;
        }
        public AtomicAdder(int increaseTimes) {
            this.increaseTimes = increaseTimes;
        }

        @Override
        public void run() {
            if(isAdd) {
                for (int i = 0; i < increaseTimes; i++) {
                    counter.incrementAndGet();
                }
            }else {
                for (int i = 0; i < increaseTimes; i++) {
                    counter.decrementAndGet();
                }
            }
        }

    }

}
